﻿//using UnityEngine;
//using System;
//using System.Collections.Generic;
//using System.IO;
//using TsiU;
//using COW;

//namespace GCommon
//{
//    public partial class ResourceManager : TSingleton<ResourceManager>
//    {
//        private class ResAsyncRequest
//        {
//            public enum ReqState : Byte
//            {
//                Waiting,
//                InProgress,
//                Completed,
//                Cancelled,
//                Recycled,
//            }

//            public uint ID;
//            public string ResPath;
//            public Action<bool, UnityEngine.Object> Callback;
//            public Type Type;
//            public ELoadAbOption ABOption;
//            public string ABPath;
//            public bool NoCache;
//            public bool NoDestroy;

//            public ReqState State;

//            public static readonly uint ID_INVALID = 0;

//            private static uint id_gen = ID_INVALID;

//            public ResAsyncRequest(string resPath, Action<bool, UnityEngine.Object> callback,
//                ELoadAbOption abOpt, string abPath, Type type, bool noCache, bool noDestroy)
//            {
//                Init(resPath, callback, abOpt, abPath, type, noCache, noDestroy);
//            }

//            public void Init(string resPath, Action<bool, UnityEngine.Object> callback,
//                ELoadAbOption abOpt, string abPath, Type type, bool noCache, bool noDestroy)
//            {
//                ID = ++id_gen;
//                ResPath = resPath;
//                Callback = callback;
//                Type = type;
//                ABOption = abOpt;
//                ABPath = abPath;
//                NoCache = noCache;
//                NoDestroy = noDestroy;

//                State = ReqState.Waiting;
//            }

//            public static void ResetIDGen() { id_gen = 0; }
//        }

//        #region AsyncResRequest Pooling
//        private readonly List<ResAsyncRequest> mResAsyncRequestPool = new List<ResAsyncRequest>();

//        private void RecycleRequest(ResAsyncRequest request)
//        {
//            request.ID = ResAsyncRequest.ID_INVALID;
//            request.ResPath = string.Empty;
//            request.ABPath = string.Empty;
//            request.Callback = null;
//            request.State = ResAsyncRequest.ReqState.Recycled;
//            mResAsyncRequestPool.Add(request);
//        }

//        private ResAsyncRequest GetResAsyncRequest(string resPath, Action<bool, UnityEngine.Object> callback,
//                ELoadAbOption abOpt, string abPath, Type type, bool noCache, bool noDestroy)
//        {
//            var count = mResAsyncRequestPool.Count;
//            if (count > 0)
//            {
//                var req = mResAsyncRequestPool[count - 1];
//                mResAsyncRequestPool.RemoveAt(count - 1);
//                req.Init(resPath, callback, abOpt, abPath, type, noCache, noDestroy);
//                return req;
//            }
//            else
//            {
//                return new ResAsyncRequest(resPath, callback, abOpt, abPath, type, noCache, noDestroy);
//            }
//        }
//        #endregion

//        private readonly LinkedList<ResAsyncRequest> mResAsyncRequestList = new LinkedList<ResAsyncRequest>();
//        private AsyncOperation mAsyncOp;
//        private bool mPendingCleanUp = false;

//        /// <summary>
//        /// The asynchronous version of GetResource by the given resource ID.
//        /// </summary>
//        /// <param name="id"></param>
//        /// <param name="callback"> The Object parameter of the callback will be a resource/asset object. </param>
//        /// <param name="notUsingABForcefully"></param>
//        /// <param name="type"></param>
//        /// <param name="noCache"></param>
//        /// </param>
//        /// <returns>
//        /// An uint identifier which you can use to cancel the async resource loading
//        /// 0 value means this resource has already been loaded or it's a failure call.
//        /// You can check the bool flag and the object in the callback.
//        /// </returns>
//        public uint GetResource(ResourceID id, Action<bool, UnityEngine.Object> callback,
//            bool notUsingABForcefully = false, Type type = null, bool noCache = false)
//        {
//            Debug.Assert(callback != null, "Don't waste the engine with null callback!");

//            if (GameVarDef.EnableGetResAsync)
//            {
//                if (m_ResourcePaths.ContainsKey(id))
//                {
//                    return EnqueueRequest(m_ResourcePaths[id], callback, ELoadAbOption.NotAb, string.Empty, type, noCache);
//                }
//                else if (m_ResourceMappingAB.ContainsKey(id))
//                {
//                    ResourceConfigAB resConfig = m_ResourceMappingAB[id];
//                    var abOpt = notUsingABForcefully ? ELoadAbOption.NotAb : resConfig.m_AbOption;
//                    string strResPath = resConfig.GetPath(ResQualityLevel);
//                    return EnqueueRequest(strResPath, callback, abOpt, resConfig.m_abPath, type, noCache);
//                }

//                Debugger.LogError("no resource mapping found: " + id);

//                callback(false, null);
//                return ResAsyncRequest.ID_INVALID;
//            }
//            else
//            {
//                var resObj = GetResource(id, notUsingABForcefully, type, noCache);
//                callback(resObj != null, resObj);
//                return ResAsyncRequest.ID_INVALID;
//            }
//        }

//        /// <summary>
//        /// The asynchronous version of GetResource by the given resource path.
//        /// </summary>
//        /// <param name="resPath"></param>
//        /// <param name="callback"></param>
//        /// <param name="notUsingABForcefully"></param>
//        /// <param name="noCache"></param>
//        /// <param name="instantiate"></param>
//        /// <returns> same explanation as the above method </returns>
//        public uint GetResource(string resPath, Action<bool, UnityEngine.Object> callback,
//            bool notUsingABForcefully = false, bool noCache = false)
//        {
//            Debug.Assert(callback != null, "Don't waste the engine with null callback!");

//            if (GameVarDef.EnableGetResAsync)
//            {
//                var ab = notUsingABForcefully ? ELoadAbOption.NotAb : ELoadAbOption.AbWithNameOrPath;
//                return EnqueueRequest(resPath, callback, ab, "", null, noCache);
//            }
//            else
//            {
//                var resObj = GetResource(resPath, notUsingABForcefully, noCache);
//                callback(resObj != null, resObj);
//                return ResAsyncRequest.ID_INVALID;
//            }
//        }

//        public uint GetResourceByNameAndAb(string resPath, string abPath,
//            Action<bool, UnityEngine.Object> callback, bool preload, bool noCache)
//        {
//            Debug.Assert(callback != null, "Don't waste the engine with null callback!");

//            if (GameVarDef.EnableGetResAsync)
//            {
//                //UnityEngine.Object resObj;
//                //if (preload && m_PreLoadedResources.TryGetValue(resPath, out resObj))
//                //{
//                //    callback(resObj != null, resObj);
//                //    return ResAsyncRequest.ID_INVALID;
//                //}

//                var abOption = string.IsNullOrEmpty(abPath) ? ELoadAbOption.NotAb : ELoadAbOption.AbWithPath;
//                return EnqueueRequest(resPath, callback, abOption, abPath, null, noCache);
//            }
//            else
//            {
//                var resObj = GetResourceByNameAndAb(resPath, abPath, preload, noCache);
//                callback(resObj != null, resObj);
//                return ResAsyncRequest.ID_INVALID;
//            }
//        }

//        /// <summary>
//        /// Cancel a waiting or in-progress asynchronously resource loading request.
//        /// </summary>
//        /// <param name="reqTicket"> The uint ticket returned when you call GetResource(). </param>
//        /// <param name="callback"> 
//        /// The exact callback action you passed to the GetResource() previously. 
//        /// If the object owning the callback get released when you wanna cancel the load, the Cancel operation won't work.
//        /// </param>
//        public void CancelAsyncResLoad(uint reqTicket, Action<bool, UnityEngine.Object> callback)
//        {
//            ResAsyncRequest reqToRemove = null;

//            var em = mResAsyncRequestList.GetEnumerator();
//            while (em.MoveNext())
//            {
//                if (em.Current.ID == reqTicket)
//                {
//                    var req = em.Current;
//                    req.Callback -= callback;
//                    if (req.Callback == null)
//                    {
//                        req.State = ResAsyncRequest.ReqState.Cancelled;
//                        if (em.Current.ID != mResAsyncRequestList.First.Value.ID || mAsyncOp == null /*impossible theoretically*/)
//                            reqToRemove = req;
//                    }
//                    break;
//                }
//            }

//            if (reqToRemove != null)
//            {
//                RecycleRequest(reqToRemove);
//                mResAsyncRequestList.Remove(reqToRemove);
//            }
//        }

//        public void UpdateAsyncLoadingRes()
//        {
//            if (!GameVarDef.EnableGetResAsync || mResAsyncRequestList == null || mResAsyncRequestList.Count <= 0)
//                return;

//            if (mAsyncOp != null && mAsyncOp.isDone)
//            {
//                if (mPendingCleanUp)
//                {
//                    CleanUpAsyncLoadResRequests();
//                    return;
//                }

//                OnAsyncGetResCompleted();
//                mAsyncOp = null;
//            }

//            PumpAsyncResLoadRequestQueue();
//        }

//        private void CleanUpAsyncLoadResRequests()
//        {
//            if (mResAsyncRequestList == null || mResAsyncRequestList.Count <= 0)
//            {
//                mAsyncOp = null;
//                mPendingCleanUp = false;
//                return;
//            }

//            if (mAsyncOp == null || mAsyncOp.isDone)
//            {
//                mAsyncOp = null;
//                mPendingCleanUp = false;
//                mResAsyncRequestList.Clear();
//                mResAsyncRequestPool.Clear();
//                ResAsyncRequest.ResetIDGen();
//                Resources.UnloadUnusedAssets();
//            }
//            else if (!mPendingCleanUp) // wait for the last async operation finished
//            {
//                var req = mResAsyncRequestList.First.Value;
//                mResAsyncRequestList.Clear();
//                mResAsyncRequestList.AddLast(req);

//                mPendingCleanUp = true;
//                return;
//            }
//        }

//        private void PumpAsyncResLoadRequestQueue()
//        {
//            while (mAsyncOp == null && mResAsyncRequestList.Count > 0)
//            {
//                var req = mResAsyncRequestList.First.Value;
//                mAsyncOp = GetResourceAsyncImpl(req);
//                // if the null goes with failure, the callback function will be called with a false flag in GetResourceAsyncImpl().
//                // So we just remove this request from the queue here and continue the while loop.
//                if (mAsyncOp == null)
//                {
//                    RecycleRequest(req);
//                    mResAsyncRequestList.RemoveFirst();
//                    continue;
//                }
//                else
//                {
//                    req.State = ResAsyncRequest.ReqState.InProgress;
//                    break;
//                }
//            }
//        }

//        private uint EnqueueRequest(string resPath, Action<bool, UnityEngine.Object> callback,
//            ELoadAbOption abOpt, string abPath = "", Type type = null, bool noCache = false, bool dontDestroy = false)
//        {
//            if (mPendingCleanUp)
//            {
//                Debugger.LogError("Resource was requested during Cleaning Up! res: " + resPath);
//                callback(false, null);
//                return ResAsyncRequest.ID_INVALID;
//            }

//            if (m_LoadedResources.ContainsKey(resPath))
//            {
//                var resObj = m_LoadedResources[resPath];
//                callback(resObj != null, resObj);
//                return ResAsyncRequest.ID_INVALID;
//            }

//            if (m_DontDestroyLoadedResources.ContainsKey(resPath))
//            {
//                var resObj = m_DontDestroyLoadedResources[resPath];
//                callback(resObj != null, resObj);
//                return ResAsyncRequest.ID_INVALID;
//            }

//            var em = mResAsyncRequestList.GetEnumerator();
//            while (em.MoveNext())
//            {
//                var req = em.Current;
//                if (string.Compare(req.ResPath, resPath, true) == 0 &&
//                    string.Compare(req.ABPath, abPath, true) == 0 &&
//                    req.ABOption == abOpt && req.Type == type &&
//                    req.NoCache == noCache && req.NoDestroy == dontDestroy)
//                {
//                    req.Callback += callback;
//                    return req.ID;
//                }
//            }

//            var reqNew = GetResAsyncRequest(resPath.ToLower(), callback, abOpt, abPath, type, noCache, dontDestroy);
//            mResAsyncRequestList.AddLast(reqNew);

//            PumpAsyncResLoadRequestQueue();

//            return reqNew.ID;
//        }

//        private AsyncOperation GetResourceAsyncImpl(ResAsyncRequest req)
//        {
//            if (m_UseAssetBundle && req.ABOption != ELoadAbOption.NotAb)
//            {
//                if (string.IsNullOrEmpty(req.ABPath))
//                    req.ABPath = req.ResPath;
//                else
//                {
//                    if (!LoadResconfDependenciesInNeed(req.ABPath))
//                    {
//                        Debugger.LogError("load Resconf dependency failed: " + req.ABPath);

//                        req.Callback(false, null);
//                        return null;
//                    }
//                }

//                string[] dependency = m_ABManifest.GetAllDependencies(req.ABPath);

//                for (int i = 0; i < dependency.Length; i++)
//                {
//                    string dependencyResName = dependency[i];

//                    if (LoadABWithoutLoadingAssets(dependencyResName, req.NoDestroy) == null)
//                    {
//                        Debugger.LogError("load dependency resource failed: " + dependencyResName);

//                        req.Callback(false, null);
//                        return null;
//                    }
//                }

//                AssetBundle ab = LoadABWithoutLoadingAssets(req.ABPath, req.NoDestroy);
//                if (ab == null)
//                {
//                    Debugger.LogError("load resource failed 4: null ab " + req.ABPath);

//                    req.Callback(false, null);
//                    return null;
//                }

//                if (ab.isStreamedSceneAssetBundle == false)
//                {
//                    var byPath = false;
//                    switch (req.ABOption)
//                    {
//                        case ELoadAbOption.AbWithPath:
//                            byPath = true;
//                            break;
//                        case ELoadAbOption.AbWithName:
//                            byPath = false;
//                            break;
//                        case ELoadAbOption.AbWithNameOrPath:
//                        default:
//                            byPath = IsPathWithSuffix(req.ResPath);
//                            break;
//                    }
//                    string resName = byPath ? req.ResPath : Path.GetFileNameWithoutExtension(req.ResPath);

//                    return ab.LoadAssetAsync<UnityEngine.Object>(resName);
//                }
//                else
//                {
//                    Debugger.LogError("not a valid resource" + req.ResPath);

//                    req.Callback(false, null);
//                    return null;
//                }
//            }
//            else
//            {
//                if (req.Type == null)
//                    return Resources.LoadAsync(req.ResPath);
//                else
//                    return Resources.LoadAsync(req.ResPath, req.Type);
//            }
//        }

//        private void OnAsyncGetResCompleted()
//        {
//            var req = mResAsyncRequestList.First.Value;

//            UnityEngine.Object resObj = null;
//            if (m_UseAssetBundle && req.ABOption != ELoadAbOption.NotAb)
//            {
//                AssetBundleRequest op = mAsyncOp as AssetBundleRequest;
//                Debug.Assert(op != null, "The AsyncOperation object is not an AssetBundleRequest. Path: " + req.ResPath);

//                resObj = op.asset;
//                if (resObj == null)
//                    Debugger.LogError("Async loading asset bundle failed with null object returned. Path: " + req.ResPath);
//            }
//            else
//            {
//                ResourceRequest op = mAsyncOp as ResourceRequest;
//                Debug.Assert(op != null, "The AsyncOperation is not an ResourceRequest. Path: " + req.ResPath);

//                resObj = op.asset;
//                if (resObj == null)
//                    Debugger.LogError("Async loading resource failed with null object returned. Path: " + req.ResPath);
//            }

//            Debug.Assert(resObj != null, "Something impossible was just happened!");

//            if (resObj == null)
//            {
//                if (req.State != ResAsyncRequest.ReqState.Cancelled && req.Callback != null)
//                    req.Callback(false, null);
//            }
//            else
//            {
//                if (req.NoDestroy)
//                    m_DontDestroyLoadedResources[req.ResPath] = resObj;
//                else
//                    m_LoadedResources[req.ResPath] = resObj;

//                if (req.State != ResAsyncRequest.ReqState.Cancelled && req.Callback != null)
//                    req.Callback(true, resObj);

//                GameFacade.ResourceLoadCount++;
//            }

//            RecycleRequest(req);
//            mResAsyncRequestList.RemoveFirst();
//        }
//    }
//}
